#include <cstdlib>
#include <cstring>
#include <iostream>

#include "vector.h"

Vector::Vector(const unsigned int &_row): row(_row){
	data = new double[row];

    // NOTA: zera tudo sem precisar da iteração
    memset(data, 0, sizeof(double)*row);
	//for(unsigned int i = 0; i < row; i++) data[i] = 0;
}

Vector::Vector(const Vector &_v): row(_v.row), data(NULL){
	*this = _v;
}

Vector::Vector(const Vector3D &_v){
	row = 3;
	data = new double[row];

	data[0] = _v.x;
	data[1] = _v.y;
	data[2] = _v.z;
}

Vector::~Vector(){
	delete[] data;
	data = 0;
}

double Vector::innerProduct(const Vector &_vector) const {
	double result = 0.0;
    // NOTA: será que _vector sempre tem a mesma dimensão do vector (this)??

	// loop mais eficiente usando aritmética de ponteiro
	const double *it     = data,
                 *theEnd = data + row,
                 *it2    = _vector.data;

    do
    {
        result += (*it++) * (*it2++);
    }while(it != theEnd);

	return result;
}

Matrix Vector::outterProduct(const Vector &_vector) const {
	Matrix result(row,_vector.getRow());

	for(unsigned int i = 0; i < row; i++){
		for(unsigned int j = 0; j < _vector.getRow(); j++){
			result[i][j] = data[i] * _vector[j];
		}
	}

	return result;
}

Vector Vector::scalarProduct(const double &_scalar) const {
	Vector result(row);

	const double *it = data,
                 *theEnd = data + row;
    double *to = result.data;
    do
    {
        *to++ = (*it++)*_scalar;
    }while( it != theEnd );

	return result;
}

double Vector::length() const {
	return ::sqrt(length_2());
}

double Vector::length_2() const {
    //const double l2 = this->innerProduct((*this));
    double l2 = 0.0;
    const double *it     = data,
                 *theEnd = data + row;

    do
    {
        //const double c = (*it++);
        l2 += (*it)*(*it);
    }while( (++it) != theEnd);

	return l2;
}

Vector Vector::normalize() const {
	double l = this->length();

    // isso introduz glithches! muito cuidado!
    // NOTA: seria melhor verificar se l é muito pequeno.
    //       Ex: if( fabs(l) < constante_muito_pequena )
    // NOTA: poderia retornar também *this, já que não há efeito

    // URGENTE! parece que há um problema aqui!

	if (l == 0.0) l = 1.0;
	//if (l == 0.0) return (*this);

	return this->scalarProduct( 1.0/l );
}

unsigned int Vector::getRow() const {
	return row;
}

void Vector::print() const{
	for(unsigned int i = 0; i < row; i++)
		std::cout << "| " << data[i] << " |" << std::endl;
}

Matrix Vector::toMatrixColumn() const {
	Matrix result(row, 1);

	for(unsigned int i = 0; i < row; i++){
		result[i][0] = data[i];
	}

	return result;
}

Vector3D Vector::toVector3D() const {
    Vector3D result;
    if (row == 3){
        result = Vector3D(data[0], data[1], data[2]);
    }
	return result;
}

double& Vector::operator[](unsigned int i) const {

    // TODO: esse check aqui pode ser lento...
	//if(i >= row) exit(1);

	return data[i];
}

Vector Vector::operator-() const{
    Vector v(row);

    const double *it = data,
                 *theEnd = data + row;
    double* to = v.data;
    do
    {
        *to++ = -(*it++);
    }while( it != theEnd );


	return v;
}

Vector& Vector::operator=(const Vector& _v1){
    if( this != &_v1 )
    {
        delete [] data;

        row = _v1.row;
        data = new double[row];

        memcpy( data, _v1.data, sizeof(double)*row );
    }

	return *this;
}

Vector Vector::operator+(const Vector& _v2) const{
	if(row != _v2.getRow())
		exit(1);

	Vector result(row);
	const double *it = data,
                 *theEnd = data + row,
                 *it2 = _v2.data;

    double* to = result.data;
    do
    {
        *to++ = (*it++) + (*it2++);
    }while( it != theEnd );

	return result;
}

Vector Vector::operator-(const Vector& _v2)const{
	if(row != _v2.getRow())
		exit(1);

	Vector result(row);
	const double *it1 = data,
                 *theEnd = data + row,
                 *it2 = _v2.data;

    double* to = result.data;
    do
    {
        *to++ = (*it1++) - (*it2++);
    }while( it1 != theEnd );

	return result;
}
